package com.hyt.it.ogt.kq.service.gov.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.util.FastJsonUtil;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.cj.service.IProjectCollectInfoOptionService;
import com.hyt.it.ogt.cj.service.IProjectCollectInfoService;
import com.hyt.it.ogt.cj.service.IProjectImageStandardService;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.exception.GovInvalidRequestException;
import com.hyt.it.ogt.kq.common.gov.constant.CodeEnum;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.utils.Constants;
import com.hyt.it.ogt.kq.common.gov.utils.tree.Tree;
import com.hyt.it.ogt.kq.service.admission.ICandidateInfoService;
import com.hyt.it.ogt.kq.service.bm.api.gov.KwGovApi;
import com.hyt.it.ogt.kq.service.gov.den.PicCompressUtil;
import com.hyt.it.ogt.kq.service.gov.den.TencentCloudCosUtil;
import com.hyt.it.ogt.kq.service.gov.mapper.SubjectMapper;
import com.hyt.it.ogt.kq.service.gov.model.entity.*;
import com.hyt.it.ogt.kq.service.gov.model.param.BmBindParam;
import com.hyt.it.ogt.kq.service.gov.model.vo.TimeRoomCandidateVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.overview.ExamManageCollectVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.overview.ExamMonitorCollectVO;
import com.hyt.it.ogt.kq.service.gov.service.*;
import com.hyt.it.ogt.kq.service.model.entity.*;
import com.hyt.it.ogt.kq.service.model.entity.admission.CandidateInfo;
import com.hyt.it.ogt.kq.service.model.param.PushCandidateParam;
import com.hyt.it.ogt.kq.service.model.param.PushCandidateSubjectParam;
import com.hyt.it.ogt.kq.service.model.vo.candidateSubject.BmTaskData;
import com.hyt.it.ogt.kq.service.model.vo.candidateSubject.OverviewBmSubjectVO;
import com.hyt.it.ogt.kq.service.model.vo.candidateSubject.OverviewBmVO;
import com.hyt.loginfo.service.IBaseLoginService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 任务总览实现类
 * </p>
 *
 * @author liuq
 * @since 2021-08-26
 */
@Service
@Slf4j
public class TaskOverviewServiceImpl implements ITaskOverviewService {

    @Autowired
    private ITaskService iTaskService;

    @Autowired
    private ITimeService iTimeService;

    @Autowired
    private ITaskCandidateAreaService iTaskCandidateAreaService;

    @Autowired
    private ISubjectService iSubjectService;

    @Autowired
    private ICandidateService iCandidateService;

    @Autowired
    private ICandidateSubjectService iCandidateSubjectService;

    @Autowired
    private ICandidatePhotoService iCandidatePhotoService;

    @Autowired
    ITimeRoomCandidateService iTimeRoomCandidateService;

    @Resource
    SubjectMapper subjectMapper;

    @Resource
    private KwGovApi bmClient;

    @Resource
    ITimeSubjectService iTimeSubjectService;

    @Resource
    private ITaskOverviewService iTaskOverviewService;

    @Resource
    private PicCompressUtil picCompressUtil;

    @Resource
    private IBaseLoginService iBaseLoginService;

    @Resource
    private ITaskRoomService iTaskRoomService;

    @Resource
    private ITaskPlaceService iTaskPlaceService;

    @Resource
    private ICustomArrangeService iCustomArrangeService;
    @Resource
    ICandidateInfoService iCandidateInfoService;
    @Resource
    IProjectCollectInfoService iProjectCollectInfoService;
    @Resource
    IProjectCollectInfoOptionService iProjectCollectInfoOptionService;
    @Resource
    IProjectImageStandardService iProjectImageStandardService;

    @Override
    public OverviewBmVO getBmInfo(String taskId, String officeId) {
        OverviewBmVO overviewBmVO = new OverviewBmVO();
        Task task = iTaskService.getById(taskId);
        // 添加横向鉴权，如果当前登录人的归属机构和任务的机构不一致直接不让访问
        if (StrUtil.isNotBlank(officeId) && StrUtil.isNotBlank(task.getOfficeId()) && !task.getOfficeId().equals(officeId) ) {
            throw new KqException(ResponseCode.GENERATE_BASE_DATA_PACKAGE_TASK_NOT_EXIST.getCode(),
                    ResponseCode.GENERATE_BASE_DATA_PACKAGE_TASK_NOT_EXIST.getMsg());
        }
        Integer type = iTaskService.taskDataType(task);
        
        if (CodeEnum.TASK_DATA_TYPE_NONE.getCode().equals(type)) {
            ApiResponse<Object> result = (ApiResponse<Object>) bmClient.loadBmglAddress();
            if (result != null && result.getData() != null) {
                overviewBmVO.setCreateUrl(result.getData().toString());
            }
            overviewBmVO.setType(type);
            return overviewBmVO;
        }

        if (CodeEnum.TASK_DATA_TYPE_IMPORT.getCode().equals(type)) {
            overviewBmVO.setType(type);
            List<OverviewBmSubjectVO> subjects = getBmSubjectVO(taskId);
            overviewBmVO.setSubjects(subjects);

            return overviewBmVO;
        }

        ApiResponse<OverviewBmVO> result = bmClient.getOverviewBmVO(task.getTaskBmId());
        log.info("从报名获取基本数据：{}", JSON.toJSONString(result));
        if (result != null && result.getData() != null) {
            overviewBmVO = result.getData();
            overviewBmVO.setProjectId(task.getTaskBmId());
        }

        //处理二维码图片路径
        //此为临时方案，后续要求报名改为华为云OBS的全路径，无需kw-gov这边自行拼接
//		String qrCode = configManager.getBmPicPath() + overviewBmVO.getQrCodeUrl();
//		overviewBmVO.setQrCodeUrl(qrCode);
		
		/*
		//获取小程序二维码
		String scene = "offId=";
		if (officeId.length() > 24) {
			scene = scene + officeId.substring(0,24);
		} else {
			scene = scene + officeId;
		}
		Map<String, Object> map = new HashMap<String, Object>();
		map = (Map<String, Object>) tpsClient.getBmQRCode(null, scene, "pages/home/index", 260);
		if (map != null && !map.isEmpty()) {
			overviewBmVO.setQrCodeUrl(map.get("obj").toString());
		}
		*/

        overviewBmVO.setType(type);

        log.info("获取报名信息数据: {}", JSON.toJSONString(overviewBmVO));
        return overviewBmVO;
    }

    @Override
    public List<Tree> getBmTree() {
        ApiResponse<List<Tree>> result = bmClient.projectTree();
        if (result == null) {
            throw new KqException(ResponseCode.ERROR_OVERVIEW_QUERY_BM_LIST_API_NULL.getCode(),
                    ResponseCode.ERROR_OVERVIEW_QUERY_BM_LIST_API_NULL.getMsg());
        }
        if (!ResponseCode.SUCCESS.getCode().equals(result.getCode()) && !CodeEnum.BM_SUCCESS_CODE.getCode().equals(result.getCode())) {
            throw new KqException(result.getCode(), "报名api接口异常");
        }

        return result.getData();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindBm(BmBindParam param) {
        String taskId = param.getTaskId();

        //1.校验数据锁定状态，锁定后不能才绑定了。
        iTaskService.isLockDataThrowException(taskId);

        //2.如果有科目数据不允许绑定
        Integer countSubject = iSubjectService.lambdaQuery().eq(Subject::getTaskId, taskId).eq(Subject::getDelFlag, false).count();
        if (countSubject > 0) {
            throw new KqException(ResponseCode.ERROR_BINDTASK_HAS_SUBJECT_DATA.getCode(),
                    ResponseCode.ERROR_BINDTASK_HAS_SUBJECT_DATA.getMsg());
        }

        //3.如果有批次数据不允许绑定
        Integer countTime = iTimeService.lambdaQuery().eq(Time::getTaskId, taskId).eq(Time::getDelFlag, false).count();
        if (countTime > 0) {
            throw new KqException(ResponseCode.ERROR_BINDTASK_HAS_TIME_DATA.getCode(),
                    ResponseCode.ERROR_BINDTASK_HAS_TIME_DATA.getMsg());
        }

        //4.如果有考生数据不允许绑定
        Integer countCandidate = iCandidateService.lambdaQuery().eq(Candidate::getTaskId, taskId).eq(Candidate::getDelFlag, false).count();
        if (countCandidate > 0) {
            throw new KqException(ResponseCode.ERROR_BINDTASK_HAS_CANDIDATE_DATA.getCode(),
                    ResponseCode.ERROR_BINDTASK_HAS_CANDIDATE_DATA.getMsg());
        }

        ApiResponse<BmTaskData> result = bmClient.bind(param.getTaskId(), param.getProjectId());
        if (result == null) {
            throw new KqException(ResponseCode.ERROR_OVERVIEW_BIND_BM_NULL.getCode(), ResponseCode.ERROR_OVERVIEW_BIND_BM_NULL.getMsg());
        }
        log.info("# 绑定云考试报名参数，获取报名任务数据: {}, {}", result.getCode(), JSON.toJSONString(result.getData()));

        if (!ResponseCode.SUCCESS.getCode().equals(result.getCode()) && !CodeEnum.BM_SUCCESS_CODE.getCode().equals(result.getCode())) {
            throw new KqException(result.getCode(), "报名api接口异常");
        }

        BmTaskData bmTaskData = result.getData();
        this.bindBm(param.getTaskId(), param.getProjectId(), bmTaskData);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbindBm(BmBindParam param, String officeId) throws Exception {
        //校验数据锁定状态，锁定后不能再解绑了。
        iTaskService.isLockDataThrowException(param.getTaskId());

        if (iTimeRoomCandidateService.countArrangedCandidate(param.getTaskId()) > 0) {
            //1.如果存在编排数据则，不允许解除
            throw new KqException(ResponseCode.ERROR_OVERVIEW_HAS_ARRANGED.getCode(), ResponseCode.ERROR_OVERVIEW_HAS_ARRANGED.getMsg());
        }
        if (iTimeSubjectService.countTimeSubject(param.getTaskId()) > 0) {
            //2.批次已绑定科目，不允许解除
            throw new KqException(ResponseCode.ERROR_OVERVIEW_HAS_TIME_SUBJECT.getCode(),
                    ResponseCode.ERROR_OVERVIEW_HAS_TIME_SUBJECT.getMsg());
        }
        //3.删除科目数据
        iSubjectService.deleteByTaskId(param.getTaskId());
        //4.删除考生区域数据
        // iTaskCandidateAreaService.deleteByTaskId(param.getTaskId());
        //5.删除考生数据
        iCandidateService.deleteByTaskId(param.getTaskId());
        //6.初始化任务数据
        //iTaskService.initTaskData(param.getTaskId(),officeId);
        //初始化考生区域数据
        iTaskCandidateAreaService.initTaskCandidateArea(param.getTaskId(), officeId);
        // 删除kw_candidate_info表老数据
        iCandidateInfoService.lambdaUpdate()
                .eq(CandidateInfo::getTaskId, param.getTaskId())
                .remove();
        // 删除cj_project_collect_info表数据
        iProjectCollectInfoService.removeByProjectId(param.getTaskId());
        // 删除cj_project_collect_info_option表数据
        iProjectCollectInfoOptionService.removeByProjectId(param.getTaskId());
        // 删除cj_project_image_standard表数据
        iProjectImageStandardService.removeByProjectId(param.getTaskId());

        ApiResponse<Object> result = bmClient.unbind(param.getTaskId(), param.getProjectId());

        if (result == null) {
            throw new KqException(ResponseCode.ERROR_OVERVIEW_UNBIND_BM_NULL.getCode(), ResponseCode.ERROR_OVERVIEW_UNBIND_BM_NULL.getMsg());
        }

        if (!ResponseCode.SUCCESS.getCode().equals(result.getCode()) && !CodeEnum.BM_SUCCESS_CODE.getCode().equals(result.getCode())) {
            throw new KqException(result.getCode(), "报名api接口异常");
        }

        this.unbindBm(param.getTaskId());
    }


    @Override
    public List<OverviewBmSubjectVO> getBmSubjectVO(String taskId) {
        return subjectMapper.getBmSubjectVO(taskId);
    }

    @Override
    public void bindBm(String taskId, String projectId, BmTaskData bmTaskData) {
        //查询原来绑定的任务
		/*Task currentTask = iTaskService.lambdaQuery()
				.eq(Task::getTaskBmId, projectId)
				.select(Task::getId)
				.last("LIMIT 1")
				.one();*/
//		if (currentTask != null) {
        //删除原来的绑定数据
        this.removeBindData(taskId);
//		}
        //更新kw_task表
        iTaskService.lambdaUpdate()
                .eq(Task::getId, taskId)
                .set(Task::getTaskBmId, projectId)
                .update();

        //插入kw_task_candidate_area表
        List<TaskCandidateArea> taskCandidateAreaList = bmTaskData.getTaskCandidateAreaList();
        if (taskCandidateAreaList != null && !taskCandidateAreaList.isEmpty()) {
            taskCandidateAreaList.stream().forEach(a -> a.setTaskId(taskId));
            iTaskOverviewService.assemblingAreaSave(taskCandidateAreaList);
        }
        //插入kw_subject表
        List<Subject> subjectList = bmTaskData.getSubjectList();
        if (subjectList != null && !subjectList.isEmpty()) {
            subjectList.stream().forEach(s -> s.setTaskId(taskId));
            iSubjectService.saveBatch(subjectList);
        }

        // 处理sonar提示
        subjectList = (subjectList == null) ? new LinkedList<>() : subjectList;

        //报名科目id和政企考务科目id对应关系map
        Map<String, String> subjectIdMap = new HashMap<>(2);
        subjectList.stream().forEach(s -> subjectIdMap.put(s.getBmSubjectId(), s.getId()));
        // 插入自定义准考证表
        List<CustomArrange> customArrangeList = new ArrayList<>();
        //插入kw_candidate表
        List<Candidate> candidateList = bmTaskData.getCandidateList();
        //报名考生id和政企考务考生id对应关系map
        Map<String, String> candidateIdMap = new HashMap<>(2);

        log.info("绑定报名任务，其中考生数据如下:{}", candidateList);
        if (candidateList != null && !candidateList.isEmpty()) {
            candidateList.stream().forEach(c -> c.setTaskId(taskId));
            iCandidateService.saveBatch(candidateList);

            candidateList.stream().forEach(item -> {
                candidateIdMap.put(item.getBmStudentId(), item.getId());

                if (StringUtils.isNotBlank(item.getAdmissionno())) {
                    // 增加自定义准考证字段同步到考务
                    CustomArrange customArrange = new CustomArrange();
                    customArrange.setCandidateId(item.getId());
                    customArrange.setTaskId(taskId);
                    customArrange.setAdmissionNumber(item.getAdmissionno());
                    customArrangeList.add(customArrange);
                }
            });

            if (!customArrangeList.isEmpty()) {
                iCustomArrangeService.saveBatch(customArrangeList);
            }
        }

        //插入kw_candidate_subject表
        List<CandidateSubject> candidateSubjectList = bmTaskData.getCandidateSubjectList();
        if (candidateSubjectList != null && !candidateSubjectList.isEmpty()) {
            candidateSubjectList.stream().forEach(cs -> {

                cs.setTaskId(taskId);
                //替换科目id和考生id
                cs.setSubjectId(subjectIdMap.get(cs.getSubjectId()))
                        .setCandidateId(candidateIdMap.get(cs.getCandidateId()));
                if (cs.getAnswerType() == null) {
                    //没有推送作答方式的情况下，为考生科目数据设置默认作答方式，保证编排能正常执行
                    cs.setAnswerType(1);
                }
                if (cs.getLanguage() == null) {
                    //没有推送语种的情况下，为考生科目数据设置默认语种，保证编排能正常执行
                    cs.setLanguage(1);
                }
            });
            iCandidateSubjectService.saveBatch(candidateSubjectList);
        }
        //插入kw_candidate_photo表
        List<CandidatePhoto> candidatePhotoList = bmTaskData.getCandidatePhotoList();
        //压缩考生照片
        String officeId = iBaseLoginService.getOfficeId();
        if (StringUtils.isBlank(officeId)) {
            officeId = bmTaskData.getOfficeId();
        }
        if (!CollectionUtils.isEmpty(candidatePhotoList)) {
            //压缩腾讯云COS照片
            this.compressTencentCosPhoto(candidatePhotoList, taskId, officeId);
            log.info("绑定是同步的考生照片：{}", JSON.toJSONString(candidatePhotoList));
            candidatePhotoList.forEach(cp -> {
                cp.setTaskId(taskId);
                //替换考生id
                cp.setCandidateId(candidateIdMap.get(cp.getCandidateId()));
            });
            iCandidatePhotoService.saveBatch(candidatePhotoList);
        }

    }

    /**
     * 压缩腾讯云COS照片
     *
     * @param candidatePhotoList 考生照片list
     * @param taskId             考试任务id
     * @param officeId           机构id
     */
    private void compressTencentCosPhoto(List<CandidatePhoto> candidatePhotoList, String taskId, String officeId) {
        //组装目标路径（存储规则：“根目录/任务编号/业务功能目录/具体的文件.xxx”）
        String rootPath = TencentCloudCosUtil.getPhotoRootPath(taskId);
        CompletableFuture.allOf(candidatePhotoList.stream()
                .map(candidatePhoto -> {
                    String targetPath = rootPath + candidatePhoto.getName();
                    log.info("# compressTencentCosPhoto 压缩腾讯云COS照片{}、{}、{}", officeId, candidatePhoto, targetPath);
                    return picCompressUtil.compressTencentCosPhotoByAsync(officeId,
                            candidatePhoto, targetPath);
                }).toArray(CompletableFuture[]::new)).join();
    }

    /**
     * 删除原来的绑定数据
     *
     * @param taskId
     */
    private void removeBindData(String taskId) {
        //删除kw_task_candidate_area表老数据
        iTaskCandidateAreaService.lambdaUpdate()
                .eq(TaskCandidateArea::getTaskId, taskId)
                .eq(TaskCandidateArea::getDelFlag, false)
                .set(TaskCandidateArea::getDelFlag, true)
                .update();

        //删除kw_subject表老数据
        iSubjectService.lambdaUpdate()
                .eq(Subject::getTaskId, taskId)
                .eq(Subject::getDelFlag, false)
                .set(Subject::getDelFlag, true)
                .update();
        //删除kw_candidate表老数据
        iCandidateService.lambdaUpdate()
                .eq(Candidate::getTaskId, taskId)
                .eq(Candidate::getDelFlag, false)
                .set(Candidate::getDelFlag, true)
                .update();
        //删除kw_candidate_subject表老数据
        iCandidateSubjectService.lambdaUpdate()
                .eq(CandidateSubject::getTaskId, taskId)
                .eq(CandidateSubject::getDelFlag, false)
                .set(CandidateSubject::getDelFlag, true)
                .update();
        //删除kw_candidate_subject表老数据
        iCandidatePhotoService.lambdaUpdate()
                .eq(CandidatePhoto::getTaskId, taskId)
                .eq(CandidatePhoto::getDelFlag, false)
                .set(CandidatePhoto::getDelFlag, true)
                .update();
    }

    @Override
    public void unbindBm(String taskId) {
        iTaskService.lambdaUpdate()
                .eq(Task::getId, taskId)
                .set(Task::getTaskBmId, null)
                .update();
    }

    @Override
    public ExamManageCollectVO getExamManageData(String taskId) {
        Task task = iTaskService.lambdaQuery()
                .select(Task::getDataLock)
                .eq(Task::getId, taskId)
                .eq(Task::getDelFlag, false)
                .last("LIMIT 1")
                .one();
        if (null == task) {
            throw new GovInvalidRequestException(ResponseCode.TASK_NOT_EXIST.getCode(),
                    "任务不存在");
        }
        // 注释是因为线上考试（比如兴业银行1月8号考试数据）sql查询需要几分钟，数据量很小也一样，导致生产上页面一直报超时，表数据量大时分单表查询速度会更快
        ExamManageCollectVO examManageCollectVO = new ExamManageCollectVO();
        int timePreNum = iTimeService.lambdaQuery().eq(Time::getTaskId, taskId).eq(Time::getDelFlag, false).count();
        int placeNum = iTaskPlaceService.lambdaQuery().eq(TaskPlace::getTaskId, taskId).eq(TaskPlace::getDelFlag, false).count();
        int roomNum = iTaskRoomService.lambdaQuery().eq(TaskRoom::getTaskId, taskId).eq(TaskRoom::getDelFlag, false).count();
        int cityNum = iTaskPlaceService.countTaskCity(taskId);
        examManageCollectVO.setTimePreNum(timePreNum);
        examManageCollectVO.setPlaceNum(placeNum);
        examManageCollectVO.setRoomNum(roomNum);
        examManageCollectVO.setCityNum(cityNum);
        //获取考生数
        Integer candidateCount = iCandidateService.lambdaQuery()
                .eq(Candidate::getTaskId, taskId)
                .eq(Candidate::getDelFlag, false)
                .count();
        //获取科目数
        Integer subjectCount = iSubjectService.lambdaQuery()
                .eq(Subject::getTaskId, taskId)
                .eq(Subject::getDelFlag, false)
                .count();
        examManageCollectVO.setCandidateNum(candidateCount)
                .setSubjectNum(subjectCount)
                .setTimeNum(timePreNum);
        // 判断任务状态
        if (Boolean.TRUE.equals(task.getDataLock())) {
            examManageCollectVO.setStatus(CodeEnum.EXAM_MANAGE_STATUS_2.getCode());
        } else if (iTimeRoomCandidateService.hasArranged(taskId, null)) {
            examManageCollectVO.setStatus(CodeEnum.EXAM_MANAGE_STATUS_1.getCode());
        } else {
            examManageCollectVO.setStatus(CodeEnum.EXAM_MANAGE_STATUS_0.getCode());
        }
        return examManageCollectVO;
    }

    @Override
    public ExamMonitorCollectVO getExamMonitorData(String taskId, String timeId) {
        if (StringUtils.isEmpty(timeId)) {
            return null;
        }
        ExamMonitorCollectVO result = iTimeService.getExamMonitorData(taskId, timeId);
        // 如果考场全部结束就结束了
        if (result.getRoomNum().intValue() > 0 && result.getRoomNum().equals(result.getExamFinishNum())) {
            //存在考场，考场数和完成考试数一致，则考试状态为完成考试
            result.setStatus(CodeEnum.EXAM_MONITOR_STATUS_2.getCode());
        } else if (result.getExamStartedNum().intValue() > 0) {
            //开始考试数大于0，则显示值正在考试中，有一个开考就显示考试中
            result.setStatus(CodeEnum.EXAM_MONITOR_STATUS_1.getCode());
        } else {
            result.setStatus(CodeEnum.EXAM_MONITOR_STATUS_0.getCode());
        }
        return result;
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Boolean pushCandidateSubject(PushCandidateSubjectParam param) throws KqException {
        //1.是否绑定任务
        String taskId = this.getTaskId(param.getProjectId());

        //2.编排锁定，不允许再推送数据。
        iTaskService.isLockDataThrowException(taskId);

        //3.查询科目信息
        Subject subject = iSubjectService.queryByBmStudentId(param.getTaskId(), param.getBmSubjectId());

        //4.数据入库
        if (Constants.PUSH_TYPE_INSERT.equals(param.getPushType())) {
            iCandidateSubjectService.insertByPushCandidateSubjectParam(param, subject);
        }

        if (Constants.PUSH_TYPE_DELETE.equals(param.getPushType())) {
            iCandidateSubjectService.deleteByPushCandidateSubjectParam(param, subject.getId());
        }
        return Boolean.TRUE;

    }

    /**
     * 获取考生编排数据
     *
     * @param projectId
     * @param studentId
     * @return
     */
    @Override
    public List<TimeRoomCandidateVO> getAdmissionInfo(String projectId, String studentId) {
        //1.是否绑定任务
        String taskId = this.getTaskId(projectId);
        //2.查找考生信息
        Candidate candidate = iCandidateService.lambdaQuery()
                .eq(Candidate::getBmStudentId, studentId)
                .eq(Candidate::getDelFlag, false)
                .select(Candidate::getId)
                .last("LIMIT 1")
                .one();
        if (candidate == null) {
            throw new KqException(ResponseCode.ERROR_BM_API_GET_CANDIDATE_NO.getCode(),
                    ResponseCode.ERROR_BM_API_GET_CANDIDATE_NO.getMsg());
        }
        //3.查询考生编排数据。
        return iTimeRoomCandidateService.selectCandidateInfo(taskId, studentId);
    }

    /**
     * 组装区域信息并保存
     *
     * @param areas
     */
    @Override
    public void assemblingAreaSave(List<TaskCandidateArea> areas) {
//		log.info("区域转换前长度：{}",areas.size());
        if (areas != null && !areas.isEmpty()) {
            Map<String, TaskCandidateArea> map = new HashMap<>(16);
            areas.stream().forEach(taskCandidateArea -> {
                map.put(taskCandidateArea.getAreaId(), taskCandidateArea);
            });

            areas.stream().forEach(taskCandidateArea -> {
                setLevelAndParentIds(map, taskCandidateArea);
            });
            //插入kw_task_candidate_area表
//			log.info("区域转换后长度：{}",areas.size());
            iTaskCandidateAreaService.saveBatch(areas);
        }
    }

    private void setLevelAndParentIds(Map<String, TaskCandidateArea> map, TaskCandidateArea taskCandidateArea) {
        String parentId = taskCandidateArea.getParentId();
        Integer level = CodeEnum.AREA_TYPE_NATION.getCode();
        StringBuilder builder = new StringBuilder();

        while (StringUtils.isNotBlank(parentId)) {
            TaskCandidateArea taskCandidateAreaNext = map.get(parentId);
            level++;
            builder.insert(0, parentId + ",");
            parentId = taskCandidateAreaNext.getParentId();
        }

        String parentIds = builder.toString();
        final String endsWithKey = ",";
        if (StringUtils.isNotBlank(parentIds) && parentIds.endsWith(endsWithKey)) {
            parentIds = parentIds.substring(0, parentIds.length() - 1);
        }
        if(StrUtil.isEmpty(taskCandidateArea.getType())) {
            taskCandidateArea.setType(String.valueOf(level));
        }
        taskCandidateArea.setParentIds(parentIds);
       

    }

    /**
     * 新增考生--
     *
     * @param param  参数
     * @param taskId 任务id
     */
    private void insertCandidate(PushCandidateParam param, String taskId) {

        // 获取考生信息
        List<CandidateInfo> bmCandidateInfoList = param.getCandidateInfoList();
        // 映射 Map<StudentId, List<CandidateInfo>>
        Map<String, List<CandidateInfo>> studentIdMappingCandidateInfoList = bmCandidateInfoList.stream()
                .collect(Collectors.groupingBy(CandidateInfo::getStudentId));

        List<Candidate> candidateListInsert = new ArrayList<>();
        List<CandidateInfo> candidateInfoList = Lists.newArrayList();
        //0.存在就不需要重复插入
        List<Candidate> candidates = iCandidateService.listByTaskId(taskId);

        Map<String, String> candidateCurrMap = candidates.stream().collect(Collectors.toMap(Candidate::getBmStudentId, Candidate::getId));
        List<CustomArrange> customArrangeList = new ArrayList<>();
        //1.插入kw_candidate表
        param.getCandidateList().stream().forEach(candidate -> {
            String bmStudentId = candidate.getBmStudentId();
            // 不存在就插入
            if (StringUtils.isBlank(candidateCurrMap.get(bmStudentId))) {
                candidate.setId(UUIDUtils.newSortUUID());
                candidate.setTaskId(taskId);
                candidateListInsert.add(candidate);
                if (StringUtils.isNotBlank(candidate.getAdmissionno())) {
                    // 增加自定义准考证字段同步到考务
                    CustomArrange customArrange = new CustomArrange();
                    customArrange.setCandidateId(candidate.getId());
                    customArrange.setTaskId(taskId);
                    customArrange.setAdmissionNumber(candidate.getAdmissionno());
                    customArrangeList.add(customArrange);
                }
                // 增加考生信息
                List<CandidateInfo> candidateInfos = studentIdMappingCandidateInfoList.getOrDefault(bmStudentId, Lists.newArrayList());
                candidateInfos.stream()
                        .forEach(data -> {
                            data.setCandidateId(candidate.getId())
                                    .setTaskId(taskId);
                        });
                CollUtil.addAll(candidateInfoList, candidateInfos);
            }
        });

        if (!CollectionUtils.isEmpty(candidateListInsert)) {
            iCandidateService.saveBatch(candidateListInsert);
        }
        if (CollUtil.isNotEmpty(candidateInfoList)) {
            iCandidateInfoService.saveBatch(candidateInfoList);
        }
        if (!CollectionUtils.isEmpty(customArrangeList)) {
            iCustomArrangeService.saveBatch(customArrangeList);
        }

        //2.新增的报名考生id和政企考务考生id对应关系map
        log.info("推送考生参数（更新后的数据），{}", JSON.toJSONString(param));
        Map<String, Candidate> candidateIdMap = param.getCandidateList().stream().
                collect(Collectors.toMap(Candidate::getBmStudentId, candidate -> candidate));


        //3.插入kw_candidate_subject表
        List<CandidateSubject> candidateSubjectList = param.getCandidateSubjectList();


        if (candidateSubjectList != null && !candidateSubjectList.isEmpty()) {

            //3.1 查询考生科目，如果存在，则不允许在此插入。
            List<CandidateSubject> candidateSubjects = iCandidateSubjectService.listByTaskId(taskId);
            Map<String, String> candidateSubjectMap = candidateSubjects.stream()
                    .collect(Collectors.toMap(CandidateSubject::getCandidateId, CandidateSubject::getSubjectId,
                            (v1, v2) -> StringUtils.join(v1, ",", v2)));

            //3.2  报名科目id和政企考务科目id对应关系map
            List<Subject> subjectList = iSubjectService.getSubjectByTaskId(taskId);
            Map<String, Subject> subjectIdMap = subjectList.stream().
                    collect(Collectors.toMap(Subject::getBmSubjectId, subject -> subject));


            //替换科目id和考生id
            List<CandidateSubject> candidateSubjectInsertList = new ArrayList<>();
            candidateSubjectList.stream().forEach(cs -> {
                Subject subject = subjectIdMap.get(cs.getSubjectId());

                Candidate candidate = candidateIdMap.get(cs.getCandidateId());
                String candidateId = candidate.getId();
                candidateId = StringUtils.isBlank(candidateId) ? candidateCurrMap.get(cs.getCandidateId()) : candidateId;
                if (StringUtils.isBlank(candidateId)) {
                    //如果为空，说明考生已经存在了
                    return;
                }

                String subjectIds = candidateSubjectMap.get(candidateId);
                if (StringUtils.isNotBlank(subjectIds) && subjectIds.contains(subject.getId())) {
                    //科目id相同，说明考生科目信息已经存在了。
                    return;
                }

                cs.setTaskId(taskId);
                cs.setSubjectId(subject.getId());
                cs.setCandidateId(candidateId);
                if (cs.getAnswerType() == null) {
                    //没有推送作答方式的情况下，为考生科目数据设置默认作答方式，保证编排能正常执行
                    cs.setAnswerType(1);
                }
                if (cs.getLanguage() == null) {
                    //没有推送语种的情况下，为考生科目数据设置默认语种，保证编排能正常执行
                    cs.setLanguage(1);
                }
                candidateSubjectInsertList.add(cs);
            });

            if (candidateSubjectInsertList != null && !candidateSubjectInsertList.isEmpty()) {
                iCandidateSubjectService.saveBatch(candidateSubjectInsertList);
            }
        }

        //4.插入kw_candidate_photo表
        List<CandidatePhoto> candidatePhotoList = param.getCandidatePhotoList();

        if (candidatePhotoList != null && !candidatePhotoList.isEmpty()) {
            List<CandidatePhoto> candidatePhotoInsertList = new ArrayList<>();
            candidatePhotoList.stream().forEach(cp -> {
                Candidate candidate = candidateIdMap.get(cp.getCandidateId());
                if (StringUtils.isNotBlank(candidate.getId()) && StringUtils.isBlank(candidateCurrMap.get(cp.getCandidateId()))) {
                    cp.setTaskId(taskId);
                    cp.setCandidateId(candidate.getId());
                    candidatePhotoInsertList.add(cp);
                }
            });

            if (!CollectionUtils.isEmpty(candidatePhotoInsertList)) {
                //压缩考生照片
                String officeId = iBaseLoginService.getOfficeId();
                if (StringUtils.isBlank(officeId)) {
                    officeId = param.getOfficeId();
                }
                this.compressTencentCosPhoto(candidatePhotoInsertList,
                        taskId, officeId);
                iCandidatePhotoService.saveBatch(candidatePhotoInsertList);
            }
        }
    }

    /**
     * 更新考生
     *
     * @param param  参数
     * @param taskId 任务id
     */
    private void updateCandidate(PushCandidateParam param, String taskId) {

        log.info("# pushCandidate-3 param:{}, taskId:{}", FastJsonUtil.getBeanToJson(param), taskId);
        // 获取考生信息
        List<CandidateInfo> bmCandidateInfoList = param.getCandidateInfoList();
        // 映射 Map<StudentId, List<CandidateInfo>>
        Map<String, List<CandidateInfo>> studentIdMappingCandidateInfoList = bmCandidateInfoList.stream()
                .collect(Collectors.groupingBy(CandidateInfo::getStudentId));
        List<Candidate> candidateList = param.getCandidateList();
        //一次只能更新一个考生
        Candidate candidate = candidateList.get(0);

        //当前考生
        Candidate currentCandidate = iCandidateService.lambdaQuery()
                .eq(Candidate::getBmStudentId, candidate.getBmStudentId())
                .eq(Candidate::getTaskId, taskId)
                .eq(Candidate::getDelFlag, false)
                .last("LIMIT 1")
                .one();

        log.info("# pushCandidate-4：currentCandidate: {}", FastJsonUtil.getBeanToJson(currentCandidate));
        if (currentCandidate != null) {
            //1.更新kw_candidate表
            currentCandidate.setProvince(candidate.getProvince())
                    .setCity(candidate.getCity())
                    .setDistrict(candidate.getDistrict())
                    .setName(candidate.getName())
                    .setSex(candidate.getSex())
                    .setIdType(candidate.getIdType())
                    .setIdNumber(candidate.getIdNumber())
                    .setBirthday(candidate.getBirthday())
                    .setPhone(candidate.getPhone())
                    .setEmail(candidate.getEmail());
            iCandidateService.updateById(currentCandidate);

            log.info("# pushCandidate-5");
            if (StringUtils.isNotBlank(candidate.getAdmissionno())) {
                CustomArrange currentCustomArrange = iCustomArrangeService.lambdaQuery()
                        .select(CustomArrange::getId, CustomArrange::getAdmissionNumber)
                        .eq(CustomArrange::getTaskId, taskId)
                        .eq(CustomArrange::getCandidateId, currentCandidate.getId())
                        .eq(CustomArrange::getDelFlag, false).last("limit 1").one();
                if (!candidate.getAdmissionno().equalsIgnoreCase(currentCustomArrange.getAdmissionNumber())) {
                    CustomArrange customArrange = new CustomArrange();
                    customArrange.setId(currentCustomArrange.getId());
                    customArrange.setAdmissionNumber(candidate.getAdmissionno());
                    iCustomArrangeService.updateById(customArrange);
                }
            }

            log.info("# pushCandidate-6");
            //2.逻辑删除kw_candidate_subject表
            iCandidateSubjectService.lambdaUpdate()
                    .eq(CandidateSubject::getCandidateId, currentCandidate.getId())
                    .eq(CandidateSubject::getDelFlag, false)
                    .set(CandidateSubject::getDelFlag, true)
                    .update();
            log.info("# pushCandidate-7");
            //3.插入kw_candidate_subject表
            List<CandidateSubject> candidateSubjectList = param.getCandidateSubjectList();
            if (candidateSubjectList != null && !candidateSubjectList.isEmpty()) {
                log.info("# pushCandidate-8");
                //报名科目id和政企考务科目id对应关系map
                List<Subject> subjectList = iSubjectService.lambdaQuery()
                        .eq(Subject::getTaskId, taskId)
                        .eq(Subject::getDelFlag, false)
                        .select(Subject::getId, Subject::getBmSubjectId)
                        .list();
                Map<String, String> subjectIdMap = new HashMap<>(16);
                subjectList.stream().forEach(s -> subjectIdMap.put(s.getBmSubjectId(), s.getId()));
                //筛选出需要更新的考生数据
                candidateSubjectList = candidateSubjectList.stream()
                        .filter(cs -> candidate.getBmStudentId().equals(cs.getCandidateId()))
                        .collect(Collectors.toList());
                //替换科目id和考生id
                candidateSubjectList.stream().forEach(cs -> {
                    cs.setTaskId(taskId)
                            .setSubjectId(subjectIdMap.get(cs.getSubjectId()))
                            .setCandidateId(currentCandidate.getId())
                            .setAnswerType(CodeEnum.COMPUTER_EXAM.getCode())
                            .setLanguage(1);
                });
                iCandidateSubjectService.saveBatch(candidateSubjectList);
            }
            //逻辑删除kw_candidate_photo表
            iCandidatePhotoService.lambdaUpdate()
                    .eq(CandidatePhoto::getCandidateId, currentCandidate.getId())
                    .eq(CandidatePhoto::getDelFlag, false)
                    .set(CandidatePhoto::getDelFlag, true)
                    .update();
            //插入kw_candidate_photo表
            List<CandidatePhoto> candidatePhotoList = param.getCandidatePhotoList();
            if (candidatePhotoList != null && !candidatePhotoList.isEmpty()) {
                //筛选出需要更新的考生数据
                candidatePhotoList = candidatePhotoList.stream()
                        .filter(cp -> candidate.getBmStudentId().equals(cp.getCandidateId()))
                        .collect(Collectors.toList());
                //替换考生id
                candidatePhotoList.stream().forEach(cp -> {
                    cp.setTaskId(taskId);
                    cp.setCandidateId(currentCandidate.getId());
                });
                //压缩考生照片
                String officeId = iBaseLoginService.getOfficeId();
                if (StringUtils.isBlank(officeId)) {
                    officeId = param.getOfficeId();
                }
                this.compressTencentCosPhoto(candidatePhotoList,
                        taskId, officeId);
                iCandidatePhotoService.saveBatch(candidatePhotoList);
            }
            // 删除kw_candidate_info表数据
            iCandidateInfoService.removeByCandidateId(currentCandidate.getId());
            // 插入kw_candidate_info表数据
            List<CandidateInfo> candidateInfoList = studentIdMappingCandidateInfoList.getOrDefault(currentCandidate.getBmStudentId(), Lists.newArrayList());
            candidateInfoList.stream()
                    .forEach(data -> {
                        data.setTaskId(currentCandidate.getTaskId())
                                .setCandidateId(currentCandidate.getId());
                    });
            iCandidateInfoService.saveBatch(candidateInfoList);

            log.info("# pushCandidate-10");
        }
    }

    /**
     * 删除考生
     *
     * @param param  参数
     * @param taskId 任务id
     */
    private void deleteCandidate(PushCandidateParam param, String taskId) {
        List<Candidate> candidateList = param.getCandidateList();
        //报名考生id
        List<String> bmStudentIdList = candidateList.stream()
                .map(Candidate::getBmStudentId)
                .collect(Collectors.toList());

        //查询所有删除的考生id
        List<String> idList = iCandidateService.lambdaQuery()
                .in(Candidate::getBmStudentId, bmStudentIdList)
                .eq(Candidate::getDelFlag, false)
                .select(Candidate::getId)
                .list()
                .stream()
                .map(Candidate::getId)
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(idList)) {
            //逻辑删除kw_candidate表
            iCandidateService.lambdaUpdate()
                    .in(Candidate::getId, idList)
                    .set(Candidate::getDelFlag, true)
                    .update();

            //逻辑删除kw_candidate_subject表
            iCandidateSubjectService.lambdaUpdate()
                    .in(CandidateSubject::getCandidateId, idList)
                    .eq(CandidateSubject::getDelFlag, false)
                    .set(CandidateSubject::getDelFlag, true)
                    .update();

            //逻辑删除kw_candidate_photo表
            iCandidatePhotoService.lambdaUpdate()
                    .in(CandidatePhoto::getCandidateId, idList)
                    .eq(CandidatePhoto::getDelFlag, false)
                    .set(CandidatePhoto::getDelFlag, true)
                    .update();
            // 删除kw_candidate_info表数据
            iCandidateInfoService.lambdaUpdate()
                    .in(CandidateInfo::getCandidateId, idList)
                    .remove();
        }
    }

    /**
     * 获取报名对应的任务Id
     *
     * @param projectId
     * @return
     */
    private String getTaskId(String projectId) throws KqException {
        //1.是否绑定任务
        Task task = iTaskService.lambdaQuery()
                .eq(Task::getTaskBmId, projectId)
                .eq(Task::getDelFlag, false)
                .select(Task::getId)
                .last("LIMIT 1")
                .one();
        if (task == null) {
            throw new KqException(ResponseCode.ERROR_BM_API_PUSH_SUBJECT_NO_TASK.getCode(),
                    ResponseCode.ERROR_BM_API_PUSH_SUBJECT_NO_TASK.getMsg());
        }
        return task.getId();
    }
}
